package oboe.controllers;

import oboe.core.ISigSlot;
import oboe.core.SigSlot;
import oboe.core.SigSlotCollection;

/**
 * ...
 * @author Felipe Manga
 */

 enum SignalType
 {
	SIG_SHORT;
	SIG_BROADCAST;
	SIG_UP;
	SIG_DOWN;
	SIG_NORMAL;
	SIG_MESSAGE(target:ISigSlot);
 }
 
 using oboe.util.ISigSlotHelper;

class Translator extends SigSlot
{
	private var signals:Hash< Array< Signal > >;

	public function new()
	{
		super();
		this.signals = new Hash< Array< Signal > >();
	}
	
	public inline function addTranslation( name:String, signal:Signal ):Translator
	{
		if ( !this.signals.exists(name) )
		{
			this.signals.set( name, new Array< Signal >() );
		}
		this.signals.get(name).push( signal );
		return this;
	}
	
	public inline function addSignalUp( name:String, newName:String, ?translator:Dynamic = null ):Translator
	{
		this.addTranslation( name, new Signal( newName, SIG_UP, translator ) );
		return this;
	}

	public inline function addSignalDown( name:String, newName:String, ?translator:Dynamic = null ):Translator
	{
		this.addTranslation( name, new Signal( newName, SIG_DOWN, translator ) );
		return this;
	}

	public inline function addSignal( name:String, newName:String, ?translator:Dynamic = null ):Translator
	{
		this.addTranslation( name, new Signal( newName, SIG_NORMAL, translator ) );
		return this;
	}

	public inline function addShortSignal( name:String, newName:String, ?translator:Dynamic = null ):Translator
	{
		this.addTranslation( name, new Signal( newName, SIG_SHORT, translator ) );
		return this;
	}

	public function addMessage( name:String, target:ISigSlot, newName:String, ?translator:Dynamic = null ):Translator
	{
		this.addTranslation( name, new Signal( newName, SIG_MESSAGE(target), translator ) );
		return this;
	}
	
	public inline function addBroadcastSignal( name:String, newName:String, ?translator:Dynamic = null ):Translator
	{
		this.addTranslation( name, new Signal( newName, SIG_BROADCAST, translator ) );
		return this;
	}
	
	override public function message(name:String, arguments:Array<Dynamic>):Void 
	{
		this._message(name, SignalDirection.SIG_DOWN, arguments);
	}
	
	override public function _message(name:String, dir:SignalDirection, arguments:Array<Dynamic>):Void 
	{
		super._message(name, dir, arguments);
		if ( this.signals.exists( name ) )
		{
			for ( sig in this.signals.get( name ) )
			{
				if ( dir != sig.direction && sig.direction != SIG_ANY ) continue;
				
				var args = arguments;
				if ( sig.argTranslator != null )
				{
					args = sig.argTranslator( args );
				}
				else if ( sig.constArgs != null )
				{
					args = sig.constArgs;
				}
				
				switch( sig.type )
				{
					case SIG_SHORT:	this.sigslot.shortSignal( sig.name, args );
					case SIG_BROADCAST: this.sigslot.broadcastSignal( sig.name, args );
					case SIG_UP: this.sigslot.signalUp( sig.name, args );
					case SIG_DOWN: this.sigslot.signalDown( sig.name, args );
					case SIG_NORMAL: this.sigslot.signal( sig.name, args );
					case SIG_MESSAGE(target): Reflect.field( target, "sigslot" ).message( sig.name, args );
				}
			}
		}
	}
	
}

class Signal
{
	public var name:String;
	public var direction:SignalDirection;
	public var type:SignalType;
	public var argTranslator:Array<Dynamic> -> Array<Dynamic>;
	public var constArgs:Array<Dynamic>;
	
	public function new( name:String, ?type:SignalType = null, ?params:Dynamic = null, ?direction:SignalDirection = null )
	{
		this.name = name;
		this.type = (type == null) ? SIG_SHORT : type;
		
		if ( params != null )
		{
			var c = Type.getClass( params );

			if ( Std.string(c) == "#Array" )
			{
				this.constArgs = cast params;
			}
			else if( Std.string( params ) == "<function>" )
			{
				this.argTranslator = cast params;
			}
			else
			{
				this.constArgs = [ params ];
			}
		}
		
		this.direction = (direction == null) ? SIG_ANY : direction;
	}
}